//===================================================
//Author      : GCL
//CreateTime  ：2/1/2019 12:50:51 PM
//Description ：
//===================================================
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

/*
 * http://www.opensource.org/licenses/lgpl-2.1.php
 * JSONObject class     only support simple data...
 * for use with Unity
 * Copyright Matt Schoen 2010
 */
using System;
using System.Globalization;



public class NullableData
{
	//Extend this class if you want to use the syntax
	//  if(myObject)
	//to check if it is not null
	public static implicit operator bool(NullableData o)
	{
		return ( object )o != null;
	}
}

public class JSONObject : NullableData
{
	const int MAX_DEPTH = 1000;
	
	public enum Type
	{
		NULL,
		STRING,
		NUMBER,
		OBJECT,
		ARRAY,
		BOOL
		
	}
	
	public JSONObject parent;
	public Type type = Type.NULL;
	public List<JSONObject> list = new List<JSONObject>();
	public List<string> keys = new List<string>();
	public string str = "";
	public double n;
	public bool b;
	
	public static JSONObject nullJO { get { return new JSONObject( JSONObject.Type.NULL ); } }
	
	public static JSONObject obj { get { return new JSONObject( JSONObject.Type.OBJECT ); } }
	
	public static JSONObject arr { get { return new JSONObject( JSONObject.Type.ARRAY ); } }
	
	public TResult GetData<TResult>()
	{
		TResult value = default(TResult);
		if( value is Int64 || value is Int32 )
			return ( TResult )( object )OutInt;
		else if( value is Single )
			return ( TResult )( object )OutFloat;
		else if( value is Vector3 )
			return ( TResult )( object )OutVector3;
		else if( value is Boolean )
			return ( TResult )( object )OutBoolean;
		else if( typeof( TResult ) == typeof(String) )
			return ( TResult )( object )OutString;
		return value;
	}
	public TResult[] GetDataArray<TResult>()
	{
		return OutArray<TResult>();
	}
	
	public int OutInt {
		get {
			return ( int )n;
		}
	}
	
	public float OutFloat {
		get {
			return ( float )n;
		}
	}
	
	public bool OutBoolean {
		get {
			return b;
		}
	}
	
	public string OutString {
		get {
			if(type == Type.NUMBER){
				return n.ToString();
			}
			else if(type == Type.BOOL)
			{
				return b.ToString();
			}
			
			return str;
		}
	}
	
	public T[] OutArray <T>(){
		
		T[] array = new T[list.Count];
		for( int i = 0; i < list.Count; i++ ) {
			//  TODO loop in list.Count
			array[i] = (T)(object)((JSONObject )list[i]).str;
		}
		return array;
	}
	
	public static string patternStr = @"\-?[0-9]+?\.[0-9]+?";
	
	public Vector3 OutVector3 {
		get {
			
			Vector3 vector3;
			string vpattern = patternStr;
			System.Text.RegularExpressions.MatchCollection matches = System.Text.RegularExpressions.Regex.Matches( str, vpattern );
			//			foreach (System.Text.RegularExpressions.Match match in matches)
			//				Console.WriteLine("'{0}' found in the source code at position {1}.",  
			//					match.Value, match.Index);
			vector3.x = float.Parse( matches[0].Value );
			vector3.y = float.Parse( matches[1].Value );
			vector3.z = float.Parse( matches[2].Value );
			//			XLogger.Log(vector3.ToString());
			return vector3;
		}
	}
	
	public bool IsNullJO {
		get {
			return type == Type.NULL;
		}
	}
	
	public JSONObject(JSONObject.Type t)
	{
		type = t;
		switch( t ) {
		case Type.ARRAY:
			list = new  List<JSONObject>();
			break;
		case Type.OBJECT:
			list = new List<JSONObject>();
			keys = new List<string>();
			break;
		}
	}
	
	public JSONObject(bool b)
	{
		type = Type.BOOL;
		this.b = b;
	}
	
	public JSONObject(float f)
	{
		type = Type.NUMBER;
		this.n = f;
	}
	
	public JSONObject(double d)
	{
		type = Type.NUMBER;
		this.n = d;
	}
	
	public JSONObject(int f)
	{
		type = Type.NUMBER;
		this.n = f;
	}
	
	public JSONObject(Array a)
	{
		type = Type.ARRAY;
		for( int i = 0; i < a.Length; i++ ) {
			//  TODO loop in a.Length
			list.Add( new JSONObject( a.GetValue( i ).ToString() ) );
		}
	}
	
	public JSONObject(Dictionary<string, string> dic)
	{
		type = Type.OBJECT;
		foreach( KeyValuePair<string, string> kvp in dic ) {
			keys.Add( kvp.Key );
			list.Add( new JSONObject( kvp.Value) );
		}
	}
	
	public string strJson = "";
	const string kSpaceText = "\n\r\t ";
	
	public JSONObject( object t )
	{
		if( t is int || t is float || t is double )
		{
			type = Type.NUMBER;
			this.n = ( double )t;
		}
		else if( t is long )
		{
			type = Type.NUMBER;
			this.n = unchecked(( long )t);
		}
		else if( t is string )
		{
			InitString( ( string )t );
		}
		
	}
	
	public JSONObject( )
	{
		
	}
	
	public JSONObject(string str)
	{	//create a new JSONObject from a string (this will also create any children, and parse the whole string)
		InitString( str );
	}
	void InitString(string str )
	{
		strJson = str;
		if( str != null )
		{
			#if( READABLE )
			str = str.Replace("\\n", "");
			str = str.Replace("\\t", "");
			str = str.Replace("\\r", "");
			str = str.Replace("\\", "");
			#endif
			if( str.Length > 0 )
			{
				if( string.Compare( str, "true", true ) == 0 )
				{
					type = Type.BOOL;
					b = true;
				}
				else if( string.Compare( str, "false", true ) == 0 )
				{
					type = Type.BOOL;
					b = false;
				}
				else if( str == "null" )
				{
					type = Type.NULL;
				}
				else if( str[0] == '"' )
				{
					type = Type.STRING;
					this.str = str.Substring( 1, str.Length - 2 );
				}
				else
				{
					
					if( Double.TryParse( str, out n))
					{
						type = Type.NUMBER;
					}
					else
					{
						
						int token_tmp = 0;
						/*
						 * Checking for the following formatting (www.json.org)
						 * object - {"field1":value,"field2":value}
						 * array - [value,value,value]
						 * value - string	- "string"
						 *		 - number	- 0.0
						 *		 - bool		- true -or- false
						 *		 - null		- null
						 */
						switch( str[0] )
						{
						case '{':
							type = Type.OBJECT;
							keys = new List<string>();
							list = new List<JSONObject>();
							break;
						case '[':
							type = JSONObject.Type.ARRAY;
							list = new List<JSONObject>();
							break;
						default:
							type = Type.STRING;
							this.str = str;
							//XLogger.LogWarning("improper JSON formatting:" + str);
							return;
						}
						int depth = 0;
						bool openquote = false;
						bool inProp = false;
						for( int i = 1; i < str.Length; i++ )
						{
							if( str[i] == '\\' )
							{
								i++;
								continue;
							}
							if( str[i] == '"' )
								openquote = !openquote;
							if( str[i] == '[' || str[i] == '{' )
								depth++;
							if( depth == 0 && !openquote )
							{
								if( str[i] == ':' && !inProp )
								{
									inProp = true;
									//try
									//{
									int StartIndex = str.IndexOf( '\"', token_tmp + 1 ) + 1;
									int EndIndex = str.LastIndexOf( '\"', i - 1 );
									if( EndIndex >=0 )
										keys.Add( str.Substring( StartIndex, EndIndex - StartIndex ) );
									else
									{
										type = Type.STRING;
										this.str = str;
										//XLogger.LogWarning("improper JSON formatting:" + str);
										return;
									}
									//}
									//catch
									//{
									//    XLogger.Log( i + " - " + str.Length + " - " + str );
									//}
									token_tmp = i;
								}
								if( str[i] == ',' )
								{
									inProp = false;
									list.Add( new JSONObject( SubStringTrimSpace( str, token_tmp + 1, i - token_tmp - 1 ) ) );
									token_tmp = i;
								}
								if( ( str[i] == ']' || str[i] == '}' ) && token_tmp + 1 != i )
									list.Add( new JSONObject( SubStringTrimSpace( str, token_tmp + 1, i - token_tmp - 1 ) ) );
							}
							if( str[i] == ']' || str[i] == '}' )
								depth--;
						}
					}
				}
			}
		}
		else
		{
			type = Type.NULL;   //If the string is missing, this is a null
		}
	}
	
	
	static string SubStringTrimSpace(string str, int StartIndex, int Length)
	{
		int EndIndex = Length < 0 ? str.Length : StartIndex + Length;
		while( StartIndex < EndIndex && kSpaceText.IndexOf( str[StartIndex] ) >= 0 ) {
			++StartIndex;
		}
		if( StartIndex < EndIndex ) {
			while( kSpaceText.IndexOf( str[EndIndex - 1] ) >= 0 ) {
				--EndIndex;
			}
		}
		return StartIndex < EndIndex ? str.Substring( StartIndex, EndIndex - StartIndex ) : string.Empty;
	}
	
	public void AddField(bool val)
	{
		Add( new JSONObject( val ) );
	}
	
	public void AddField(float val)
	{
		Add( new JSONObject( val ) );
	}
	
	public void AddField(int val)
	{
		Add( new JSONObject( val ) );
	}
	
	public void Add(JSONObject obj)
	{
		if( obj ) {		//Don't do anything if the object is null
			if( type != JSONObject.Type.ARRAY ) {
				type = JSONObject.Type.ARRAY;		//Congratulations, son, you're an ARRAY now
				//XLogger.LogWarning("tried to add an object to a non-array JSONObject.  We'll do it for you, but you might be doing something wrong.");
			}
			list.Add( obj );
		}
	}
	
	public void AddField(string name, bool val)
	{
		AddField( name, new JSONObject( val ) );
	}
	
	public void AddField(string name, float val)
	{
		AddField( name, new JSONObject( val ) );
	}
	
	public void AddField(string name, int val)
	{
		AddField( name, new JSONObject( val ) );
	}
	
	public void AddField(string name, string val)
	{
		AddField( name, new JSONObject { type = JSONObject.Type.STRING, str = val } );
	}
	
	public void AddField(string name, JSONObject obj)
	{
		if( obj ) {		//Don't do anything if the object is null
			if( type != JSONObject.Type.OBJECT ) {
				type = JSONObject.Type.OBJECT;		//Congratulations, son, you're an OBJECT now
				//XLogger.LogWarning("tried to add a field to a non-object JSONObject.  We'll do it for you, but you might be doing something wrong.");
			}
			keys.Add( name );
			list.Add( obj );
		}
	}
	
	public void SetField(string name, JSONObject obj)
	{
		if( HasField( name ) ) {
			int index = GetFieldIndex( name );
			list.RemoveAt( index );
			keys.RemoveAt( index );
		}
		AddField( name, obj );
	}
	
	public JSONObject GetField(string name)
	{
		if( type == JSONObject.Type.OBJECT )
			for( int i = 0; i < keys.Count; i++ )
				if( ( string )keys[i] == name )
					return ( JSONObject )list[i];
		return JSONObject.nullJO;
	}
	
	public int GetFieldIndex(string name)
	{
		if( type == JSONObject.Type.OBJECT )
			for( int i = 0; i < keys.Count; i++ )
				if( ( string )keys[i] == name )
					return i;
		return -1;
	}
	
	public bool HasField(string name)
	{
		if( type == JSONObject.Type.OBJECT )
			for( int i = 0; i < keys.Count; i++ )
				if( ( string )keys[i] == name )
					return true;
		return false;
	}
	
	public void Clear()
	{
		type = JSONObject.Type.NULL;
		list.Clear();
		keys.Clear();
		str = "";
		n = 0;
		b = false;
	}
	
	public JSONObject Copy()
	{
		return new JSONObject( print() );
	}
	/*
	 * The Merge function is experimental. Use at your own risk.
	 */
	public void Merge(JSONObject obj)
	{
		MergeRecur( this, obj );
	}
	
	static void MergeRecur(JSONObject left, JSONObject right)
	{
		if( right.type == JSONObject.Type.OBJECT ) {
			for( int i = 0; i < right.list.Count; i++ ) {
				if( right.keys[i] != null ) {
					string key = ( string )right.keys[i];
					JSONObject val = ( JSONObject )right.list[i];
					if( val.type == JSONObject.Type.ARRAY || val.type == JSONObject.Type.OBJECT ) {
						if( left.HasField( key ) )
							MergeRecur( left[key], val );
						else
							left.AddField( key, val );
					} else {
						if( left.HasField( key ) )
							left.SetField( key, val );
						else
							left.AddField( key, val );
					}
				}
			}
		}// else left.list.Add(right.list);
	}
	
	public string print()
	{
		return print( 0 );
	}
	
	public string getString()
	{
		return str;
	}
	
	public string print(int depth)
	{	//Convert the JSONObject into a stiring
		if( depth++ > MAX_DEPTH ) {
			Debug.Log( "reached max depth!" );
			return "";
		}
		string str = "";
		switch( type ) {
		case Type.STRING:
			str = "\"" + this.str + "\"";
			break;
		case Type.NUMBER:
			str += n;
			break;
		case JSONObject.Type.OBJECT:
			if( list.Count > 0 ) {
				str = "{";
				#if(READABLE)	//for a bit more readability, comment the define above to save space
				str += "\n";
				depth++;
				#endif
				for( int i = 0; i < list.Count; i++ ) {
					string key = ( string )keys[i];
					JSONObject obj = ( JSONObject )list[i];
					if( obj ) {
						#if(READABLE)
						for(int j = 0; j < depth; j++)
							str += "\t"; //for a bit more readability
						#endif
						str += "\"" + key + "\":";
						str += obj.print( depth ) + ",";
						#if(READABLE)
						str += "\n";
						#endif
					}
				}
				#if(READABLE)
				str = str.Substring(0, str.Length - 1);
				#endif
				str = str.Substring( 0, str.Length - 1 );
				str += "}";
			} else
				str += "null";
			break;
		case JSONObject.Type.ARRAY:
			str = "[";
			if( list.Count > 0 ) {
				#if(READABLE)
				str += "\n"; //for a bit more readability
				depth++;
				#endif
				foreach( JSONObject obj in list ) {
					if( obj ) {
						#if(READABLE)
						for(int j = 0; j < depth; j++)
							str += "\t"; //for a bit more readability
						#endif
						str += obj.print( depth ) + ",";
						#if(READABLE)
						str += "\n"; //for a bit more readability
						#endif
					}
				}
				#if(READABLE)
				str = str.Substring(0, str.Length - 1);
				#endif
				str = str.Substring( 0, str.Length - 1 );
			}
			str += "]";
			break;
		case Type.BOOL:
			if( b )
				str += "true";
			else
				str += "false";
			break;
		case Type.NULL:
			str = "null";
			break;
		}
		return str;
	}
	
	public JSONObject this[ int index ] {
		get { return ( JSONObject )list[index]; }
	}
	
	public int Count {
		get {
			return list.Count;
		}
	}
	
	public JSONObject this[ string index ] {
		get { return GetField( index ); }
		set {
			if( this.HasField( index ) ) {
				this.SetField( index, value );
			} else {
				AddField( index, value );
			}
		}
	}
	
	public override bool Equals(object obj)
	{
		return ( GetHashCode() == obj.GetHashCode() );
	}
	
	public override int GetHashCode()
	{
		return ToString().GetHashCode();
	}
	
	public override string ToString()
	{
		return print();
	}
	
	public Dictionary<string, string> ToDictionary()
	{
		if( type == Type.OBJECT ) {
			Dictionary<string, string> result = new Dictionary<string, string>();
			for( int i = 0; i < list.Count; i++ ) {
				JSONObject val = ( JSONObject )list[i];
				switch( val.type ) {
				case Type.STRING:
					result.Add( ( string )keys[i], val.str );
					break;
				case Type.NUMBER:
					result.Add( ( string )keys[i], val.n + "" );
					break;
				case Type.BOOL:
					result.Add( ( string )keys[i], val.b + "" );
					break;
				default:
					Debug.LogWarning( "Omitting object: " + ( string )keys[i] + " in dictionary conversion" );
					break;
				}
			}
			return result;
		} else
            Debug.LogWarning( "Tried to turn non-Object JSONObject into a dictionary" );
		return null;
	}
	
}

public class JsonObjectEquality : IEqualityComparer<JSONObject>
{
	public bool Equals(JSONObject x, JSONObject y)
	{
		if( x.type != y.type )
			return false;
		switch( x.type ) {
		case JSONObject.Type.NULL:
		{
			return x.Equals( y );
		}
		case JSONObject.Type.BOOL:
		{
			return x.b == y.b;
		}
		case JSONObject.Type.NUMBER:
		{
			return Mathf.Abs( ( float )( x.n - y.n ) ) < 0.0001;  
		}
		case JSONObject.Type.STRING:
		{
			return x.str == y.str;
		}
		case JSONObject.Type.ARRAY:
		{
			if( x.list.Count != y.list.Count )
				return false;
			foreach( System.Object obj in x.list ) {
				JSONObject jsObj = ( JSONObject )( obj );
				if( !y.list.Contains( jsObj ) )
					return false;
			}
			foreach( System.Object obj in y.list ) {
				JSONObject jsObj = ( JSONObject )( obj );
				if( !x.list.Contains( jsObj ) )
					return false;
			}
			return true;
		}
		case JSONObject.Type.OBJECT:
		{
			foreach( string keyName in x.keys ) {
				if( !y.HasField( keyName ) )
					return false;
				if( !Equals( x[keyName], y[keyName] ) )
					return false;
			}
			foreach( string keyName in y.keys ) {
				if( !x.HasField( keyName ) )
					return false;
				if( !Equals( y[keyName], x[keyName] ) )
					return false;
			}
			return true;
		}
		}
		return true;
	}
	
	public int GetHashCode(JSONObject obj)
	{
		if( obj == null ) {
			return 0;
		} else {
			return obj.ToString().GetHashCode();
		}
	}
}